﻿package main

import (
	"encoding/binary"
	"fmt"
	"io"
	"log"
	"net"

	"github.com/hashicorp/yamux"
)

type TcpBridgeNode struct {
	Uid       int    // 1000
	SubDomain string // test1 => test1.gate.com
	Key       string // abc12345

	Channels      int            // 总的通道数量
	Port          int            // 监听端口
	TcpYamuxConn  *yamux.Session // 复用的Control Tcp conn
	TcpBindServer *net.Listener  // 对外公开的服务端口
	CmdChan       chan int       // block 控制管道
}
type TcpBridgeHandler struct {
	ClientNodes map[int]*TcpBridgeNode // id => gate
	tcpServer   *net.Listener          // 服务监听端口
}

// 需要固定size = 16
type ClientLoginReq struct {
	Version [2]byte  // 版本号: 1.0
	Uid     int32    // 账户ID: 1000
	Key     [10]byte // 随机生成的Key: abc_123456
}

// 需要固定size = 12
type ClientLoginResp struct {
	Uid  int32 // 账户ID: 1000
	Code int32 // 状态: 0 成功，400/500...其他看情况
	Idx  int32 // 控制通道ID
}

func (h *TcpBridgeHandler) tcpBridgeRun(addr string) error {
	s, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	defer s.Close()
	h.tcpServer = &s

	for {
		c, err := s.Accept()
		if err != nil {
			break
		}
		go h.HandlerNew(c)
	}
	return nil
}

func (h *TcpBridgeHandler) HandlerNew(c net.Conn) error {
	defer c.Close()

	// 获取client的请求信息
	var cLoginReq ClientLoginReq
	if err := binary.Read(c, binary.LittleEndian, &cLoginReq); err != nil {
		return err
	}

	log.Println("client login:", cLoginReq)

	// check Uid and key
	node := TcpBridgeNode{
		Uid:           int(cLoginReq.Uid),
		SubDomain:     "test",
		Key:           "abc_123456", // == cLoginReq.Key
		Channels:      1,
		Port:          50010,
		TcpYamuxConn:  nil,
		TcpBindServer: nil,
	}
	if true {
		// 给应答
		cLoginResp := ClientLoginResp{
			Uid:  cLoginReq.Uid,
			Code: 0, // 其他case给非零值
			Idx:  0,
		}
		if err := binary.Write(c, binary.LittleEndian, cLoginResp); err != nil {
			return err
		}

		log.Println("node ready for tcp", node)
		// 创建TcpControl channel
		// https://github.com/hashicorp/yamux
		session, err := yamux.Client(c, nil)
		if err != nil {
			return err
		}
		node.TcpYamuxConn = session
		defer session.Close()

		// Open tcp server
		s, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%v", node.Port))
		if err != nil {
			return err
		}
		node.TcpBindServer = &s
		defer s.Close()

		log.Println("new tcp server with port:", node.Port)

		// process new connection
		for {
			s := *node.TcpBindServer
			c, err := s.Accept()
			if err != nil {
				continue
			}
			log.Println("new tcp comein: ", c, "streams:", node.TcpYamuxConn.NumStreams())

			// Open a new stream
			// 服务器作为客户端，每次open会trigger用户那边accept，从而产生一个新的stream
			stream, err := node.TcpYamuxConn.Open()
			if err != nil {
				log.Println("yamux stream error:", err)
				return err
			}

			// copy
			go h.IoCopy(c, stream)
		}
		return nil

	}
	return nil
}

func (h *TcpBridgeHandler) IoCopy(c1, c2 net.Conn) {
	defer c1.Close()
	defer c2.Close()
	log.Println("yamux stream start", c1.RemoteAddr())

	// copy
	go io.Copy(c1, c2)
	io.Copy(c2, c1)

	log.Println("yamux stream close", c1.RemoteAddr())
}
